package vsvm.math.statistics;

import java.util.ArrayList;
import java.util.Iterator;

import org.apache.commons.math.MathException;

import vsvm.classifier.AbstractTestSet;
import vsvm.classifier.TestCase;
import vsvm.exceptions.IncompatableTestSetsException;
import vsvm.math.CoreMath;
import vsvm.math.statistics.distributions.NormalDistribution;

public class AUCTest {
	
	private static final String corFile = "correlation.dat";
	
	private AbstractTestSet testSet1;
	private AbstractTestSet testSet2;
	private ROCAnalysis roc1;
	private ROCAnalysis roc2;
	private double auc1;
	private double auc2;
	private double se1;
	private double se2;
	private double zvalue;
	
	private double significance = 0.05;
	
	
	public AUCTest(AbstractTestSet testSet1, AbstractTestSet testSet2)
	throws IncompatableTestSetsException {		
		this.testSet1 = testSet1; this.testSet2 = testSet2;
		if (testSet1.getTestCasesCount() != testSet2.getTestCasesCount()) {
			throw new IncompatableTestSetsException("Not equal test cases counts");
		}
		ROCAnalysis roc1 = new ROCAnalysis(testSet1);
		ROCAnalysis roc2 = new ROCAnalysis(testSet2);
		
		auc1 = roc1.getWStatistic(); auc2 = roc2.getWStatistic();
		se1 = roc1.getStandardError();
		se2 = roc2.getStandardError();
		
		double positive = this.testSet1.getPositiveClass();
		double negative = this.testSet2.getNegativeClass();
		int pcount1 = this.testSet1.getActualPositivesCount();
		int ncount1 = this.testSet1.getActualNegativesCount();
		
		int pcount2 = this.testSet2.getActualPositivesCount();
		int ncount2 = this.testSet2.getActualNegativesCount();
		
		double [] positives1 = new double[pcount1]; 
		double [] negatives1 = new double[ncount1];
		
		double [] positives2 = new double[pcount2]; 
		double [] negatives2 = new double[ncount2];
		this.prepareProbArrays(testSet1, positives1, negatives1);
		this.prepareProbArrays(testSet2, positives2, negatives2);
		
		double rp = Characteristics.getSpearmanCorrelation(positives1, positives2);
		double rn = Characteristics.getSpearmanCorrelation(negatives1, negatives2);
		
		double r   = (rp + rn)/2;
		double auc = (auc1 + auc2)/2;		
		
		r = McNeilCorrMatrix.getCorrelation(auc, r);
		zvalue = (auc1 - auc2)/Math.sqrt(se1*se1 + se2*se2 - 2*r*se1*se2);
	}
	
	private void prepareProbArrays(AbstractTestSet testSet, double[] posArray, double[] negArray){
		
		double positive = testSet.getPositiveClass();
		
		int i = 0; int j = 0;		
		Iterator <TestCase> it = testSet.getTestCases();
		while (it.hasNext()) {
			TestCase tcase = it.next();
			if (Math.abs(tcase.getActualClass() - positive) < CoreMath.EPS)
				posArray[i++] = tcase.getProbabilities()[1];
			else
				negArray[j++] = tcase.getProbabilities()[1];
		}
	}
	
	/**
	 *  P value for two sided test:
	 *  p = 2*P(Z >= |Estimate|)
	 *  
	 *  Default significance level - 0.05
	 */
	
	public double getPValue() throws MathException {
		NormalDistribution nd = new NormalDistribution();
		return 2*(1 - nd.cumulativeProbability(Math.abs(zvalue)));		
	}
	
	public double getZValue() {
		return this.zvalue;
	}
	
	public double[] getStandardErrors() {
		return new double[]{se1, se2};
	}
	
	public double[] getAUCs() {
		return new double[]{auc1, auc2};
	}
	
	public AbstractTestSet[] getTestSets() {
		return new AbstractTestSet[] { this.testSet1, this.testSet2 };
	}
	
	public double getSignificanceLevel(){
		return this.significance;
	}
	
	public void setSignificanceLevel(double significance) {
		this.significance = significance;
	}
	
}
